﻿using System;
using System.Configuration;
using System.IO;
using System.ServiceProcess;
using System.Threading;

namespace OpenVpnLauncher
{
    public partial class OpenVpnLauncher : ServiceBase
    {
        private ServiceControllerStatus _status;
        private Timer _timer;
        private bool _working;
        private string _serviceNameToWatch;
        private string _foldersToMakeReadOnly;
        private int _pollingIntervalInSecond;

        #region constructor

        /// <summary>
        /// Initializes a new instance of the <see cref="OpenVpnLauncher"/> class.
        /// </summary>
        public OpenVpnLauncher()
        {
            InitializeComponent();

            //
            this.CanPauseAndContinue = true;
            this.CanShutdown = true;
            this.CanStop = true;
        }

        #endregion

        #region service caller

        /// <summary>
        /// When implemented in a derived class, executes when a Start command is sent to the service by the Service Control Manager (SCM) or when the operating system starts (for a service that starts automatically). Specifies actions to take when the service starts.
        /// </summary>
        /// <param name="args">Data passed by the start command.</param>
        protected override void OnStart(string[] args)
        {
            //
            _serviceNameToWatch = ConfigurationManager.AppSettings["ServiceNameToWatch"];
            _foldersToMakeReadOnly = ConfigurationManager.AppSettings["FoldersToMakeReadOnly"];
            _pollingIntervalInSecond = Convert.ToInt32(ConfigurationManager.AppSettings["PollingIntervalInSecond"]);

            // setup callback
            var tmrCallBack = new TimerCallback(timer_TimerCallback);

            // initialize time
            _timer = new Timer(tmrCallBack);

            //
            OnContinue();
        }

        /// <summary>
        /// When implemented in a derived class, executes when a Stop command is sent to the service by the Service Control Manager (SCM). Specifies actions to take when a service stops running.
        /// </summary>
        protected override void OnStop()
        {
            _timer.Dispose();
            ProcessService(true);
            _status = ServiceControllerStatus.Stopped;
        }

        /// <summary>
        /// When implemented in a derived class, executes when a Pause command is sent to the service by the Service Control Manager (SCM). Specifies actions to take when a service pauses.
        /// </summary>
        protected override void OnPause()
        {
            // Manually stop the timer...
            _timer.Change(Timeout.Infinite, Timeout.Infinite);
            //
            _status = ServiceControllerStatus.Paused;
        }

        /// <summary>
        /// When implemented in a derived class, <see cref="M:System.ServiceProcess.ServiceBase.OnContinue"/> runs when a Continue command is sent to the service by the Service Control Manager (SCM). Specifies actions to take when a service resumes normal functioning after being paused.
        /// </summary>
        protected override void OnContinue()
        {
            // have the timer start in 1 second, and then fire according to settings
            _timer.Change(new TimeSpan(0, 0, 1), new TimeSpan(0, 0, _pollingIntervalInSecond));

            //
            _status = ServiceControllerStatus.Running;
        }

        #endregion

        #region private methods

        private void timer_TimerCallback(object state)
        {
            // only process if not already working and not paused
            if (_working || _status == ServiceControllerStatus.Paused)
            {
                return;
            }

            // mark work start
            _working = true;

            // first we check status of service; if stopped, we run it.
            ProcessService();

            // mark work start
            _working = false;
        }

        private void ProcessService(bool stop = false)
        {
            //
            if (string.IsNullOrEmpty(_serviceNameToWatch))
            {
                return;
            }

            //
            var serviceController = new ServiceController(_serviceNameToWatch);
            switch (serviceController.Status)
            {
                case ServiceControllerStatus.Stopped:
                case ServiceControllerStatus.StopPending:
                    // service is stopped
                    RemoveFilesReadOnlyAttribute(_foldersToMakeReadOnly);
                    serviceController.Start();
                    serviceController.Refresh();
                    break;

                default:
                    // force stop
                    if(stop)
                    {
                        serviceController.Stop();
                        serviceController.Refresh();
                        RemoveFilesReadOnlyAttribute(_foldersToMakeReadOnly);
                    }

                    break;
            }
        }


        /// <summary>
        /// Removes the files read only attribute.
        /// </summary>
        /// <param name="folderPaths">The folder paths.</param>
        private static void RemoveFilesReadOnlyAttribute(string folderPaths)
        {
            if (String.IsNullOrEmpty(folderPaths))
            {
                return;
            }

            //
            foreach (var foldePath in folderPaths.Split(';'))
            {
                foreach (var filePath in Directory.GetFiles(foldePath.Trim()))
                {
                    if((File.GetAttributes(filePath) & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
                    {
                        // clear readonly attribute
                        File.SetAttributes(filePath, File.GetAttributes(filePath) & ~FileAttributes.ReadOnly);                        
                    }
                }
            }
        }

        #endregion

    }
}
